package com.yu.judge.codesandebox.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrBuilder;
import com.yu.judge.codesandebox.CodeSandbox;
import com.yu.judge.model.ExcuteResponse;
import com.yu.judge.model.ExecuteRequest;
import com.yu.judge.model.JudgeInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * java代码沙箱实现测试第一版
 */
@Slf4j
public class JavaCodeSandbox_test implements CodeSandbox {

    private static final String GLOBAL_CODE_PATH_NAME = "tempCode";
    private static final String GLOBAL_JAVA_CLASS_NAME = "Main.java";
    public static final Long MAX_RUN_TIME=10000L;

    private static final String SECURITY_MANAGER_CLASS_NAME = "SafeController";


    @Override
    public ExcuteResponse excuteCode(ExecuteRequest exCuteCodeRequest) {
        ExcuteResponse excuteResponse = new ExcuteResponse();
        List<String> input = exCuteCodeRequest.getInput();
        String language = exCuteCodeRequest.getLanguage();
        String code = exCuteCodeRequest.getCode();
        if (code.isEmpty()) {
            JudgeInfo judgeInfo = new JudgeInfo();
            judgeInfo.setTime(0L);
            judgeInfo.setMemory(0L);
            judgeInfo.setCodeSize(0L);
            judgeInfo.setMessage("代码长度为空");
            judgeInfo.setSandboxMessage("编译失败");
            excuteResponse.setMessage("编译失败");
            excuteResponse.setJudgeInfo(judgeInfo);
            return excuteResponse;
        }

        String userDir = System.getProperty("user.dir");  //C:\java_work\myspringbootproject\oj-codesandbox
        String globalCodePathName = userDir + File.separator + GLOBAL_CODE_PATH_NAME;

        if (!FileUtil.exist(globalCodePathName)) {
            FileUtil.mkdir(globalCodePathName);
        }

        String userCodeParentPath = globalCodePathName + File.separator + UUID.randomUUID();
        String userCodePath = userCodeParentPath + File.separator + GLOBAL_JAVA_CLASS_NAME;
        File useeCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);

        /**
         * 编译代码
         *
         *  * 最重要的  最重要的  做重要的   安全控制   栈爆了目前未解决  （没给提示信息 陷入了无限卡死  后面实在解决不了 可以用无限卡死程序的解决办法）
         *          * 解决了堆暴了  无限卡死程序   读本地程序   写本地程序  后面这俩需要java安全管理器（没学呢 好像是继承一个什么管理类）
         */


        String filePath = useeCodeFile.getAbsoluteFile().toString();//获取java编译器
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();//编译
        int result = 0;
        try {
            result = javaCompiler.run(null, null, null, filePath);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (result != 0) {
            System.out.println("编译失败");
            //编译失败了直接删除文件见   编译都失败了 留着干什么呢
            // TODO:  为了调试才采用注释 后面给他开启

            /*if(useeCodeFile.getParentFile()!=null) {
                FileUtil.del(userCodeParentPath);
                System.out.println("删除");
            }*/
            JudgeInfo judgeInfo = new JudgeInfo();
            judgeInfo.setTime(0L);
            judgeInfo.setMemory(0L);
            judgeInfo.setCodeSize(useeCodeFile.length());
            judgeInfo.setSandboxMessage("编译失败");
            excuteResponse.setMessage("编译失败");
            excuteResponse.setJudgeInfo(judgeInfo);
            return excuteResponse;

        } else {
            System.out.println("编译成功");
            //运行
            String runCode = String.format("java -Dfile.encoding=utf-8 %s -Djava.security.manager=%s", useeCodeFile.getAbsoluteFile(),SECURITY_MANAGER_CLASS_NAME);
            try {
                long maxRuntime = 0L;
                long maxMemory=0L;  //解决内存（暂未解决先把思路理清）
                List<String> ans = new ArrayList<>();
                JudgeInfo judgeInfo = new JudgeInfo();
                judgeInfo.setSandboxMessage("编译成功");
                judgeInfo.setMessage("运行成功");
                judgeInfo.setCodeSize(useeCodeFile.length());
                excuteResponse.setStatus(3);
                for (String s : input) {
                    StopWatch stopWatch = new StopWatch();
                    Process runProcess = Runtime.getRuntime().exec(runCode);  //执行
                    new Thread(() -> {   //监听线程
                        try {
                            Thread.sleep(MAX_RUN_TIME);
                            if (runProcess.isAlive()) {
                                runProcess.destroy();
                                judgeInfo.setMessage("运行超时");
                                judgeInfo.setTime(10000L);
//                                judgeInfo.setMemory();  //目前用代码长度代替
//                                judgeInfo.setStackLimit();/
                                excuteResponse.setJudgeInfo(judgeInfo);
                            }
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }).start();

                    stopWatch.start();
                    OutputStream outputStream = runProcess.getOutputStream();
                    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                    InputStream inputStreamSuccess = runProcess.getInputStream();
                    InputStream inputStreamError = runProcess.getErrorStream();
                    BufferedReader bufferedSuccess = new BufferedReader(new InputStreamReader(inputStreamSuccess));
                    BufferedReader bufferedError = new BufferedReader(new InputStreamReader(inputStreamError));
                    //输入参数的
                    String[] split = s.split(" ");
                    for (String splits : split) {
                        outputStreamWriter.write(splits + "\n");
                        outputStreamWriter.flush();
                    }
                    int runValue = runProcess.waitFor();
                    stopWatch.stop();
                    long runTime = stopWatch.getLastTaskTimeMillis();
                    maxRuntime = Math.max(maxRuntime, runTime);
                    //read
                    if (runValue == 0) {
                        String outputInfoSuccess;
                        StringBuilder infoSuccess = new StringBuilder();
                        while ((outputInfoSuccess = bufferedSuccess.readLine()) != null)   //read line by line
                        {
                            infoSuccess.append(outputInfoSuccess);
                        }
                        ans.add(infoSuccess.toString());
                    }
                    else {
                        String outputInfoSuccess;
                        StringBuilder infoSuccess = new StringBuilder();
                        while ((outputInfoSuccess = bufferedSuccess.readLine()) != null)   //read line by line
                        {
                            infoSuccess.append(outputInfoSuccess);
                        }
                        String outputInfoError;
                        StringBuilder infoError = new StringBuilder();
                        while ((outputInfoError = bufferedError.readLine()) != null)   //read line by line
                        {
                            infoError.append(outputInfoError);
                        }
                        StringBuilder infoAll = new StringBuilder();
                        infoAll.append(infoSuccess).append(infoError);


                        // TODO: 为了测试    后面在开
                        /*if (useeCodeFile.getParentFile() != null) {
                            FileUtil.del(userCodeParentPath);
                            System.out.println("删除");
                            //判题嘛  有一个测试用例错了  就不用往下测了这个代码肯定不对
                            return null;
                        }*/
                        if(!judgeInfo.getMessage().isEmpty()){
                            if(judgeInfo.getMessage().equals("运行超时")){
                                judgeInfo.setTime(MAX_RUN_TIME);
                                judgeInfo.setMemory(100L);
                                judgeInfo.setStackLimit(100L);
                                judgeInfo.setCodeSize(useeCodeFile.length());
                                judgeInfo.setSandboxMessage(infoAll.toString());
                                log.info("错误执行信息+{}",infoAll.toString());
                                excuteResponse.setMessage(infoAll.toString());
                                excuteResponse.setJudgeInfo(judgeInfo);
                                return excuteResponse;
                            }
                            //待定
                            if(judgeInfo.getMessage().equals("内存超出")){
                                judgeInfo.setTime(MAX_RUN_TIME);
                                judgeInfo.setMemory(100L);
                                judgeInfo.setStackLimit(100L);
                                judgeInfo.setCodeSize(useeCodeFile.length());
                                judgeInfo.setSandboxMessage(infoAll.toString());
                                log.info("错误执行信息+{}",infoAll.toString());
                                excuteResponse.setMessage(infoAll.toString());
                                excuteResponse.setJudgeInfo(judgeInfo);
                                return excuteResponse;
                            }
                        }
                        judgeInfo.setMessage("编译成功运行失败");
                        judgeInfo.setCodeSize(useeCodeFile.length());
                        judgeInfo.setSandboxMessage(infoAll.toString());
                        log.info("错误执行信息+{}",infoAll.toString());
                        excuteResponse.setMessage(infoAll.toString());
                        excuteResponse.setJudgeInfo(judgeInfo);
                        return excuteResponse;
                    }
                    outputStream.close();
                    outputStreamWriter.close();
                    inputStreamError.close();
                    inputStreamSuccess.close();
                    bufferedSuccess.close();
                    runProcess.destroy();
                }

                excuteResponse.setOutput(ans);
                excuteResponse.setMessage("运行成功");
                judgeInfo.setTime(maxRuntime);
                judgeInfo.setMemory(100L);
                judgeInfo.setStackLimit(100L);
                excuteResponse.setJudgeInfo(judgeInfo);
                excuteResponse.setStatus(2);

            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        return excuteResponse;
    }
}
